package containerizedengine

import (
	"context"
	"github.com/containerd/containerd"
	"github.com/containerd/containerd/images"
	"github.com/containerd/containerd/remotes/docker"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"io"
	"xemo.top/docker/cli/cli/config/types"
	clitypes "xemo.top/docker/cli/types"
	"xemo.top/docker/docker/pkg/jsonmessage"
)

// NewClient返回一个新的容器化引擎客户端
// NewClient returns a new containerizedengine client
// 此客户端可用于管理dockerd作为containerd上的容器运行的生命周期。
// This client  can be used to manage the lifecycle of dockerd running as a container on containerd.
func NewClient(sockPath string) (clitypes.ContainerizedClient, error) {
	if sockPath == "" {
		sockPath = containerdSockPath
	}
	cclient, err := containerd.New(sockPath)
	if err != nil {
		return nil, err
	}
	return &baseClient{
		cclient: cclient,
	}, nil
}

// Close 关闭将关闭基础客户端
// Close will close the underlying clients
func (c *baseClient) Close() error {
	return c.cclient.Close()
}

func (c *baseClient) pullWithAuth(ctx context.Context, imageName string, out clitypes.OutStream, authConfig *types.AuthConfig) (containerd.Image, error) {
	resolver := docker.NewResolver(docker.ResolverOptions{Credentials: func(string) (string, string, error) {
		return authConfig.Username, authConfig.Password, nil
	}})

	ongoing := newJobs(imageName)
	pctx, stopProgress := context.WithCancel(ctx)
	progress := make(chan struct{})
	bufin, bufout := io.Pipe()
	go func() {
		showProgress(pctx, ongoing, c.cclient.ContentStore(), bufout)
	}()

	go func() {
		jsonmessage.DisplayJSONMessagesToStream(bufin, out, nil)
		close(progress)
	}()
	h := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
		if desc.MediaType != images.MediaTypeDockerSchema1Manifest {
			ongoing.add(desc)
		}
		return nil, nil
	})
	image, err := c.cclient.Pull(ctx, imageName, containerd.WithResolver(resolver), containerd.WithImageHandler(h), containerd.WithPullUnpack)
	stopProgress()
	if err != nil {
		return nil, err
	}
	<-progress
	return image, nil
}
